package ro.flashbite.game_engine.view.sheet.gpu
{
	import com.greensock.TweenMax;
	import com.greensock.easing.Linear;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.PixelSnapping;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	import ro.flashbite.errors.ArgumentIncompatibleError;
	import ro.flashbite.game_engine.view.sheet.BaseSpriteSheet;
	import ro.flashbite.helpers.MathHelpers;
	import ro.flashbite.helpers.MovieClipHelpers;
	
	/**
	 * Base class that can act like a MovieClip, but is uses a SpriteSheet bitmapData and a vector of Rectangles to draw visuals with extra speed.
	 * Can only play frame bases animation; No FrameLabels are recognized; This class use visibility of all bitmaps to show current frame
	 *  
	 * v 1.0
	 * 
	 * @author Adrian Barbu
	 */	
	public class SimpleSpriteSheetGPU extends BaseSpriteSheet
	{
		protected static const DEFAULT_POINT:	Point = new Point(); //used for performance in blit function
		
		private static const DEFAULT_SPEED	:	Number = 1 / 30; //30 frames per second
		
		private var _animation		:	TweenMax;
		
		protected var _currentFrame	:	uint;
		protected var _ccurentFrame	:	Number; //used on render
		protected var _totalFrames	:	uint;
		
		protected var _allBmpFrames:	Vector.<Bitmap>; // holds all frames as bitmaps
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function SimpleSpriteSheetGPU(sheetBmpData:BitmapData, settings:XML)
		{
			_constructor(sheetBmpData, settings);
		}
		private function _constructor(sheetBmpData:BitmapData, settings:XML):void
		{
			if (sheetBmpData == null && settings == null) return; //called from children; no code will be executed here
			
			_allBmpFrames = new Vector.<Bitmap>();
			//create and save bitmaps
			var rectangles:Vector.<Rectangle> = new Vector.<Rectangle>(),
				xmlListSlices:XMLList = settings.slices.slice as XMLList,
				i:uint,
				len:uint = xmlListSlices.length(),
				xmlSlice:XML,
				rect:Rectangle,
				bmpData:BitmapData,
				bmp:Bitmap;
			
			for (i = 0; i < len; i++) {
				xmlSlice = xmlListSlices[i];
				
				rect = new Rectangle(Number(xmlSlice.@x), Number(xmlSlice.@y), Number(xmlSlice.@width), Number(xmlSlice.@height));
				bmpData = new BitmapData(rect.width, rect.height, true, 0x00FFFFFF);
				bmpData.copyPixels(sheetBmpData, rect, DEFAULT_POINT);
				bmp = new Bitmap(bmpData, PixelSnapping.NEVER, true);
				
				_allBmpFrames.push(bmp);
				
				this.addChild(bmp); 
				
				bmp.visible = false;
			}
			
			//set total frames
			_totalFrames = _allBmpFrames.length;
			
			//stop it on frame 1
			_currentFrame = 1; // set this for non extra-check on set currentFrame 
			gotoAndStop(1);
			
			//disable interaction
			disable();
		}
		
		// ================================================================================================================================
		// ISleepWakeupDestroy
		// ================================================================================================================================
		
		override public final function sleep():void
		{
			if (_animation) _animation.pause();
		}
		
		override public final function wakeup():void
		{
			if (_animation) _animation.resume();
		}
		
		override public final function destroy():void
		{
			if (_animation) {
				_animation.kill();
				_animation = null;
			}
			
			if (_allBmpFrames) {
				for each (var bmp:Bitmap in _allBmpFrames) {
					if (bmp && bmp.bitmapData) bmp.bitmapData.dispose();
				}
				_allBmpFrames = null;
			}
			
			MovieClipHelpers.removeAllChildren(this);
		}
		
		// ================================================================================================================================
		// IInteractive
		// ================================================================================================================================
		
		override public final function enable()	: void 	{ this.mouseEnabled = this.mouseChildren = true; 	}
		override public final function disable(): void 	{ this.mouseEnabled = this.mouseChildren = false; 	}
		
		// ================================================================================================================================
		// MovieClip like behaviour
		// ================================================================================================================================
		
		/**
		 * Basic play
		 * 
		 * @param from : start frame
		 * @param to : end frame
		 * @param speed : speed of animation -> 1 is normal, 2 is fast , .5 is slow, etc...
		 * @param repeatCount : num on repeats
		 * @param onCompleteCall : function to call on complete
		 * @return (Number) duration in seconds of the anim
		 */		
		override public final function playFromTo(from:Object, to:Object, speed:Number = 1, repeatCount:int = 0, onCompleteFunc:Function = null, onCompleteParams:Array = null):Number
		{
			if (from is uint && to is uint) {
				const fromm:uint = from as uint,
					too:uint = to as uint;
				if ( MathHelpers.isBetween(fromm, 1, _totalFrames) && MathHelpers.isBetween(too, 1, _totalFrames) ) {
					//stop on from frame
					gotoAndStop(fromm);
					//calculate time
					var difFrames:uint;
					if (fromm >= too) {
						difFrames = fromm - too;
					} else difFrames = too - fromm;
					//calculate time
					const time:Number = difFrames * (DEFAULT_SPEED / speed);
					//start animation
					_animation = TweenMax.to(
												this, 
												time, 
												{ 
													__ccurentFrame:too, //use number values that will be rounded
													ease:Linear.easeNone, 
													onComplete:onCompleteFunc, 
													onCompleteParams:onCompleteParams,
													repeat:repeatCount 
												}
											);
					//return time
					return time;
				} else throw new ArgumentIncompatibleError("from ||&& to");
			} else throw new ArgumentIncompatibleError("from ||&& to");
			//return error
			return -1;
		}
		
		override public final function gotoAndStop(frame:Object):void
		{
			if (frame is uint) {
				currentFrame = frame as uint;
				_ccurentFrame = _currentFrame;
				stop();
			} else {
				throw new ArgumentIncompatibleError("frame");
			}
		}
		
		override public final function stop():void
		{
			if (_animation) {
				_animation.kill();
			}
		}
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		override public final function copyPixelsFromFrame(frame:uint, bmpData:BitmapData, point:Point):void
		{
			var currentBmp:Bitmap = _allBmpFrames[_currentFrame - 1];
			//copy current bmpData from visible item
			bmpData.copyPixels(currentBmp.bitmapData, new Rectangle(0, 0, currentBmp.width, currentBmp.height), point);
		}
		
		// ================================================================================================================================
		// GETTERS, SETTERS
		// ================================================================================================================================
		
		override public final function get isPlaying():Boolean { return ( _animation && (_animation.active) && (_animation.paused == false)); }
		
		override public final function get __ccurentFrame()		  : Number { return _ccurentFrame; }
		override public final function set __ccurentFrame(v:Number): void 
		{ 
			_ccurentFrame = v; 
			//_ccurentFrame = Math.round(_ccurentFrame);
			//faster version of Math.round -> on mobile (Android) 66.6% faster; on mobile (iPhone/iPad) 18.6% faster; on PC : 57.71% faster 
			_ccurentFrame = (_ccurentFrame + 0.5) >> 0;
			
			if (_ccurentFrame < 1) _ccurentFrame = 1;
			else if (_ccurentFrame > _totalFrames) _ccurentFrame = _totalFrames;
			
			currentFrame = _ccurentFrame;
		}
		
		override public final function get currentFrame():uint { return _currentFrame; }
		override public final function set currentFrame(v:uint):void
		{
			//if (_currentFrame == value) return; //no performance saving in this check
			
			//hide last frame
			//_currentFrame is 1 based, _allBmpFrames are 0 based
			_allBmpFrames[_currentFrame - 1].visible = false;
			
			//save currentFrame 
			_currentFrame = v;
			
			//set visibility to true to currentFrame
			//_currentFrame is 1 based, _allBmpFrames are 0 based
			_allBmpFrames[_currentFrame - 1].visible = true;
			
			//trace("Current frame : " + _currentFrame);
		}
		
		override public final function get totalFrames():uint { return _totalFrames; }
		
		override public final function get animation():TweenMax { return _animation; }
	}
}